/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
/*
第一步：实现基本框架
    new关键字表明Promise可以写成一个类，接受一个执行器函数作为参数，执行器函数立即执行；
    Promise有三种状态：pendding/fullfilled/rejected, 只能由pendding变为fullfilled，或者pendding变为rejected;
    执行器函数接受resolve和reject两个函数作为参数，它们的作用是更改Promise状态。resolve被调用时将状态变为fullfilled，reject被调用时将状态变为rejected;
    从then方法的调用方式来看，得知它是原型方法。它接受两个函数作为参数，分别是成功回调函数和失败回调函数，状态变为fullfilled时执行成功回调函数，状态变为失败时执行失败回调函数。

    let promise = new Promise ((resolve, reject) => {
        resolve()
    })
    promise.then(value => {
        console.log('成功')
    }, (reason) => {
        console.log('失败')
    })

第二步： then方法中的成功回调函数，应该可以获取到执行器中调用resolve函数时传的参数；then方法中的失败回调函数，应该可以获取到执行器中调用reject函数时传的参数；
        我们把这两个数据存储起来，方便then方法使用。

    let promise = new Promise ((resolve, reject) => {
        resolve(100)
    })
    promise.then(value => {
        console.log(value)
    }, (reason) => {
        console.log(reason)
    })

第三步： 现在所有代码都是同步的, 在调用then时，还是pendding状态，根本不会调用成功回调或者失败回调。
        要实现异步方式，我们可以在then函数调用时，判断如果是pendding状态，就先把回调函数存储起来，等状态改变时再去调用。

    let promise = new Promise ((resolve, reject) => {
        setTimeout(() => {
            resolve(100)
        }, 2000)
    })
    promise.then(value => {
        console.log(value)
    }, (reason) => {
        console.log(reason)
    })

第四步： 同一个promise, 可以重复调用then方法。目前的代码，同步情况下是可以满足需求的。
        但在异步情况时，在then方法中，第二个回调函数会覆盖第一个，2秒后只会输出"第二个成功回调“。
        所以我们应该用数组把所有回调函数都存储起来, 等状态改变时遍历该数组进行调用。

    let promise = new Promise ((resolve, reject) => {
        setTimeout(() => {
            resolve(100)
        }, 2000)
    })
    promise.then(value => {
        console.log(value)
    }, (reason) => {
        console.log(reason)
    })
    promise.then(value => {
        console.log('第二个成功回调')
    }, (reason) => {
        console.log(reason)
    })

第五步： Promise最大的优势就是可以实现链式调用。
        可以看出，要想实现链式调用，调用then方法后，必定是要返回一个Promise对象;
        所以在then方法中，我们要创建一个Promise的实例，并且返回它；
        在实例化一个Promise时，执行器方法是立即执行的，所以我们可以把then方法中的代码放入这个promise实例的执行器函数中;
        在这个执行器函数中，我们需要获取上一个Promise回调函数中的返回值:
         1. 返回了一个值，那么 then 返回的 Promise 将会成为fulfilled状态，并且将返回的值作为成功回调函数的参数值。
         2. 如果没有返回任何值，那么 then 返回的 Promise 将会成为fulfilled状态，并且成功回调函数的参数值为 undefined

    let promise = new Promise ((resolve, reject) => {
        resolve(100)
    })
    promise.then(value = > {
        console.log(value) // 输出100
        return 200
    }).then(value => {
        console.log(value) // 输出200
    })

第六步： Promise链式调用中，then方法回调函数还可以返回一个Promise对象：
        1. 如果它是fulfilled状态，那么我们返回的promise实例将成为fulfilled状态，成功回调函数的参数值 = 这个Promise对象成功回调函数的参数值
        2. 如果它是rejected状态，那么我们返回的promise实例将成为rejected状态，失败回调函数的参数值 = 这个Promise对象失败回调函数的参数值
        3. 不管上一个promise是成功回调还是失败回调，需要判断的逻辑都是一样的，所以这部分代码可以封装成一个函数
        4. then方法的pendding状态，也是相同的逻辑，完善一下

    let promise = new Promise ((resolve, reject) => {
        setTimeout(()=>{
            resolve(100)
        }, 3000)
    })

    let p2 = new Promise ((resolve, reject) => {
        resolve(200)
    })

    promise
        .then(value => {
            console.log(value) // 100
            return p2
            }, () => {}
        )
        .then(value => {
            console.log(value) // 200
            }, () => {}
        )     

第七步： 捕获错误，当代码执行过程中报错了，我们需要捕获错误，将Promise改为rejected状态；
        需要捕获错误的地方：执行器函数报错, then中的成功回调函数报错， then中的失败回调函数报错
    
    let promise = new Promise ((resolve, reject) => {
        throw 'errorrrrrr'
    })

    promise.then(() => {}, reason => {
        console.log(reason) // errorrrrrr
    })

    ——————————————————————————————————————————————————————————

    let promise = new MyPromise ((resolve, reject) => {
    resolve(100)
    })

    promise
    .then((value) => {
        throw 'errrrrrrrrr'
        }, () => {})
    .then(() => {}, (reason) => {
        console.log(reason) // errrrrrrrrr
    })   

第八步： 在then方法的回调函数中，如果返回了当前promise对象，会报错：typeError: Chaining cycle detected for promise；
        所以我们应该去then方法中判断返回值是否等于当前对象；
        由于在then方法执行过程中，当前对象还没有返回，所以结果必然是不等于当前对象；所以我们需要对这个判断逻辑进行异步处理

    let promise = new MyPromise ((resolve, reject) => {
        resolve(100)
    })
    let p2 = promise.then(value => {
        console.log(value)
        return p2
    })

    p2.then(() => {}, reason => {
        console.log(reason) // typeError: Chaining cycle detected for promise
    })

第九步： then方法中的两个参数，即成功回调函数和失败回调函数，都是可选参数
        如果两个参数都不传， then() , 会原样往后传

    let promise = new Promise ((resolve, reject) => {
        resolve(100)
    })

    promise.then().then().then(value => {
        console.log(value) // 100
    })

    ——————————————————————————————————————————————————————————

    let promise = new Promise ((resolve, reject) => {
        reject("失败")
    })

    promise.then().then().then(value => {
        console.log(value)
    }, reason => {
        console.log(reason) // 失败
    })

第十步： 实现promise.all方法，它接受一个数组作为参数，返回一个promise对象

    const promise1 = new Promise((resolve, reject) => resolve(3));
    const promise2 = 42;
    const promise3 = new Promise((resolve, reject) => {
        setTimeout(resolve, 100, 'foo');
    })

    Promise.all([promise1, promise2, promise3]).then((values) => {
        console.log(values);
    })

第十一步： 实现promise.resolve方法，它的作用就是把给定的值转为promise对象
          参数可以是普通值时等同于
          funciton(value){
              return new Promise((resolve, reject) => resolve(value))     
          } 
          参数是Promise对象或者thenable对象时原样后传

    const promise1 = Promise.resolve(123)
    promise1.then((value) => {
        console.log(value) // expected output: 123
    })

第十二步： 实现promise.finally方法，它是一个原型方法，它的作用是不管promis执行成功还是失败，都执行finally中的回调函数，最后返回一个Promise对象

    function p1 () {
        return new Promise((resolve, reject) => {
            resolve(100)
        })
    }
    p1 ().finally(() => {
        console.log('finally')
    })

第十二步： 实现promise.catch方法，它是一个原型方法，它的作用是then方法不传错误回调函数时，用catch方法也能捕获错误。

    function p1 () {
      return new Promise((resolve, reject) => {
          reject('失败')
      })
    }
    p1 ()
      .then(value => console.log(value))
      .catch(reason => console.log(reason))

    
 */



const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败    
class HandPromise {
    constructor (executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    // 状态
    status = PENDING

    // 执行器函数中resolve函数传过来的值
    value = undefined

    // 执行器函数中reject函数传过来的值
    reason = undefined

    // 成功回调
    successCallback = []

    // 失败回调
    failCallback = []

    // 将状态改为fulfilled
    resolve = value => {
        if (this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value
        if (this.successCallback.length) {
            for (let i = 0; i < this.successCallback.length; i++) {
                this.successCallback[i](this.value)
            }  
        }
    }
    // 将状态改为rejected
    reject = reason => {
        if (this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        if (this.failCallback.length) {
            for (let i = 0; i < this.failCallback.length; i++) {
                this.failCallback[i](this.value)
            }  
        }
    }

    then (successCallback, failCallback) {
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : reason => reason
        let promise2 = new HandPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                try {
                    setTimeout(() => {
                        let x = successCallback(this.value)
                        resolvePromise (promise2, x, resolve, reject)
                        }, 0)
                } catch (e) {
                    reject(e)
                }
            } 
            if (this.status === REJECTED) {
                try {
                    setTimeout(() => {
                        let x = failCallback(this.reason)
                        resolvePromise (promise2, x, resolve, reject)
                    }, 0)     
                } catch (e) {
                    reject(e)
                }
            }
            if (this.status == PENDING) {
                this.successCallback.push(() => {
                    try {
                        setTimeout(() => {
                            let x = successCallback(this.value)
                            resolvePromise (promise2, x, resolve, reject)
                        }, 0)     
                    } catch (e) {
                        reject(e)
                    }               
                })
                this.failCallback.push(() => {
                    try {
                        setTimeout(() => {
                            let x = failCallback(this.reason)
                            resolvePromise (promise2, x, resolve, reject)
                        }, 0)    
                    } catch (e) {
                        reject(e)
                    }
                    
                })
            }
        })
        return promise2
    }
    finally (callback) {
        return this.then(value => {
          return HandPromise.resolve(callback()).then(() => value)
        }, reason => {
          return HandPromise.resolve(callback()).then(() => reason)
        })
    }
    catch (failCallback) {
        return this.then (undefined, failCallback)
    }
    static all (array) {
        let result = []
        let index = 0 // 为了解决for循环中有异步操作导致结果不正确的问题
        return new HandPromise((resolve, reject) => {
            function addData (key, value) {
                result[key] = value
                index++;
                if(index === array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i]
                if (current instanceof HandPromise) {
                    // promise对象
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值
                    addData(i, array[i])
                }
            }
            
        })
    }
    static resolve(value) {
        if (value instanceof HandPromise) return value
        //return new HandPromise ((resolve) => { resolve(value) })
        return new HandPromise(resolve => resolve(value)); // 上一行的简写
    }
}
function resolvePromise (promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('typeError: Chaining cycle detected for promise'))
     }
    if (x instanceof HandPromise) {
        x.then(value => {
            resolve(value)
        }, reason => {
            reject(reason)
        })
    } else {
        resolve(x)
    }
}
module.exports = HandPromise;